// --------------------------------------------------------------------------
// Downloaded from: 
//		http://opensource.apple.com/source/gdb/gdb-573/src/gdb/i386-stub.c
// Modified by Steve Matafonov on 05 aug 2012
// --------------------------------------------------------------------------

/****************************************************************************

		THIS SOFTWARE IS NOT COPYRIGHTED

   HP offers the following for use in the public domain.  HP makes no
   warranty with regard to the software or it's performance and the
   user accepts the software "AS IS" with all faults.

   HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD
   TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES
   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.

****************************************************************************/

/****************************************************************************
 *  Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $
 *
 *  Module name: remcom.c $
 *  Revision: 1.34 $
 *  Date: 91/03/09 12:29:49 $
 *  Contributor:     Lake Stevens Instrument Division$
 *
 *  Description:     low level support for gdb debugger. $
 *
 *  Considerations:  only works on target hardware $
 *
 *  Written by:      Glenn Engel $
 *  ModuleState:     Experimental $
 *
 *  NOTES:           See Below $
 *
 *  Modified for 386 by Jim Kingdon, Cygnus Support.
 *
 *  To enable debugger support, two things need to happen.  One, a
 *  call to set_debug_traps() is necessary in order to allow any breakpoints
 *  or error conditions to be properly intercepted and reported to gdb.
 *  Two, a breakpoint needs to be generated to begin communication.  This
 *  is most easily accomplished by a call to breakpoint().  Breakpoint()
 *  simulates a breakpoint by executing a trap #1.
 *
 *  The external function exceptionHandler() is
 *  used to attach a specific handler to a specific 386 vector number.
 *  It should use the same privilege level it runs at.  It should
 *  install it as an interrupt gate so that interrupts are masked
 *  while the handler runs.
 *
 *  Because gdb will sometimes write to the stack area to execute function
 *  calls, this program cannot rely on using the supervisor stack so it
 *  uses it's own stack area reserved in the int array remcomStack.
 *
 *************
 *
 *    The following gdb commands are supported:
 *
 * command          function                               Return value
 *
 *    g             return the value of the CPU registers  hex data or ENN
 *    G             set the value of the CPU registers     OK or ENN
 *
 *    mAA..AA,LLLL  Read LLLL bytes at address AA..AA      hex data or ENN
 *    MAA..AA,LLLL: Write LLLL bytes at address AA.AA      OK or ENN
 *
 *    c             Resume at current address              SNN   ( signal NN)
 *    cAA..AA       Continue at address AA..AA             SNN
 *
 *    s             Step one instruction                   SNN
 *    sAA..AA       Step one instruction from AA..AA       SNN
 *
 *    k             kill
 *
 *    ?             What was the last sigval ?             SNN   (signal NN)
 *
 * All commands and responses are sent with a packet which includes a
 * checksum.  A packet consists of
 *
 * $<packet info>#<checksum>.
 *
 * where
 * <packet info> :: <characters representing the command or response>
 * <checksum>    :: < two hex digits computed as modulo 256 sum of <packetinfo>>
 *
 * When a packet is received, it is first acknowledged with either '+' or '-'.
 * '+' indicates a successful transfer.  '-' indicates a failed transfer.
 *
 * Example:
 *
 * Host:                  Reply:
 * $m0,10#2a               +00010203040506070809101112131415#42
 *
 ****************************************************************************/

#include "stdafx.h"

/************************************************************************
 *
 * external low-level support routines
 */
/* Number of registers.  */
#define NUMREGS	16

/* Number of bytes of registers.  */
#define NUMREGBYTES (NUMREGS * 4)

/* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/
/* at least NUMREGBYTES*2 are needed for register packets */
#define BUFMAX 400

#define STACKSIZE 10000

#define BREAKPOINT() __asm int 0x03

extern "C"
{
	extern void putDebugChar(char);	/* write a single character      */
	extern int getDebugChar();	/* read and return a single char */
	extern void exceptionHandler(int, void (*)());	/* assign an exception handler   */

	void _remcomHandler();
	/* Address of a routine to RTE to if we get a memory fault.  */
	static void (*volatile mem_fault_routine) () = NULL;

	/* Indicate to caller of mem2hex or hex2mem that there has been an
	   error.  */
	static volatile int mem_err = 0;
	/************************************************************************/


	static char initialized;  /* boolean flag. != 0 means we've been initialized */

	int     remote_debug;
	/*  debug >  0 prints ill-formed commands in valid packets & checksum errors */

	static const char hexchars[]="0123456789abcdef";

	enum regnames {EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI,
			   PC /* also known as eip */,
			   PS /* also known as eflags */,
			   CS, SS, DS, ES, FS, GS};

	/*
	 * these should not be static cuz they can be used outside this module
	 */
	int registers[NUMREGS];


	int remcomStack[STACKSIZE/sizeof(int)];
	static int* stackPtr = &remcomStack[STACKSIZE/sizeof(int) - 1];

	
	/* Put the error code here just in case the user cares.  */
	int gdb_i386errcode;
	/* Likewise, the vector number here (since GDB only gets the signal
	   number through the usual means, and that's not very specific).  */
	int gdb_i386vector = -1;


	/***************************  ASSEMBLY CODE MACROS *************************/
	/* Restore the program's registers (including the stack pointer, which
	   means we get the right stack and don't have to worry about popping our
	   return address and any stack frames and so on) and return.  */
	void __declspec(naked) return_to_prog ()
	{
		__asm mov  ss,  word  ptr [registers + 44]
		__asm mov  esp, dword ptr [registers + 16]
		__asm mov  ecx, dword ptr [registers + 4]
		__asm mov  edx, dword ptr [registers + 8]
		__asm mov  ebx, dword ptr [registers + 12]
		__asm mov  ebp, dword ptr [registers + 20]
		__asm mov  esi, dword ptr [registers + 24]
		__asm mov  edi, dword ptr [registers + 28]
		__asm mov  ds,  word  ptr [registers + 48]
		__asm mov  es,  word  ptr [registers + 52]
		__asm mov  fs,  word  ptr [registers + 56]
		__asm mov  gs,  word  ptr [registers + 60]
		__asm mov  eax, dword ptr [registers + 36]
		__asm push eax  /* saved eflags */
		__asm mov  eax, dword ptr [registers + 40]
		__asm push eax  /* saved cs */
		__asm mov  eax, dword ptr [registers + 32]
		__asm push eax  /* saved eip */
		__asm mov  eax, dword ptr [registers]
	/* use iret to restore pc and flags together so
		that trace flag works right.  */
		__asm iret
	}


	/* GDB stores segment registers in 32-bit words (that's just the way
	   m-i386v.h is written).  So zero the appropriate areas in registers.  */
	#define SAVE_REGISTERS1() \
	  __asm mov dword ptr [registers + 0], eax			\
	  __asm mov dword ptr [registers + 4], ecx			\
	  __asm mov dword ptr [registers + 8], edx			\
	  __asm mov dword ptr [registers + 12], ebx		\
	  __asm mov dword ptr [registers + 20], ebp		\
	  __asm mov dword ptr [registers + 24], esi		\
	  __asm mov dword ptr [registers + 28], edi		\
	  __asm xor eax, eax								\
	  __asm mov word  ptr [registers + 48], ds			\
	  __asm mov word  ptr [registers + 50], ax			\
	  __asm mov word  ptr [registers + 52], es			\
	  __asm mov word  ptr [registers + 54], ax			\
	  __asm mov word  ptr [registers + 56], fs			\
	  __asm mov word  ptr [registers + 58], ax			\
	  __asm mov word  ptr [registers + 60], gs			\
	  __asm mov word  ptr [registers + 62], ax

	#define SAVE_ERRCODE()								\
	  __asm pop ebx										\
	  __asm mov gdb_i386errcode, ebx

	#define SAVE_REGISTERS2() \
	  __asm pop ebx /* old eip */			  		    \
	  __asm mov dword ptr [registers + 32], ebx			\
	  __asm pop ebx	 /* old cs */			  			\
	  __asm mov dword ptr [registers + 40], ebx			\
	  __asm mov word  ptr [registers + 42], ax			\
	  __asm pop ebx	 /* old eflags */		  			\
	  __asm mov dword ptr [registers +36], ebx			\
	  /* Now that we've done the pops, we can save the stack pointer.");  */   \
	  __asm mov word  ptr [registers + 44], ss			\
	  __asm mov word  ptr [registers + 46], ax     		\
	  __asm mov dword ptr [registers + 16], esp

	

	void __declspec(naked) mem_fault()
	{
		/* OK to clobber temp registers; we're just going to end up in set_mem_err.  */
		/* Pop error code from the stack and save it.  */
		__asm
		{
			pop	 eax
			mov gdb_i386errcode, eax
	
			pop	 eax /* eip */
		/* We don't want to return there, we want to return to the function
		   pointed to by mem_fault_routine instead.  */
			mov eax, mem_fault_routine
			pop	 ecx /* cs (low 16 bits; junk in hi 16 bits).  */
			pop	 edx /* eflags */

		/* Remove this stack frame; when we do the iret, we will be going to
		   the start of a function, so we want the stack to look just like it
		   would after a "call" instruction.  */
			leave

		/* Push the stuff that iret wants.  */
			push edx /* eflags */
			push ecx /* cs */
			push eax /* eip */

		/* Zero mem_fault_routine.  */
			xor  eax, eax
			mov	 mem_fault_routine, eax
	
			iret
		}
	}

	/* This function is called when a i386 exception occurs.  It saves
	 * all the cpu regs in the registers array, munges the stack a bit,
	 * and invokes an exception handler (remcom_handler).
	 *
	 * stack on entry:                       stack on exit:
	 *   old eflags                          vector number
	 *   old cs (zero-filled to 32 bits)
	 *   old eip
	 *
	 */
	void __declspec(naked) _catchException3()
	{
		SAVE_REGISTERS1();
		SAVE_REGISTERS2();
	
		__asm push 3

		_remcomHandler();
	}

	/* Same thing for exception 1.  */
	void __declspec(naked) _catchException1()
	{
		SAVE_REGISTERS1();
		SAVE_REGISTERS2();
		__asm push 1
		_remcomHandler();
	}

	/* Same thing for exception 0.  */
	void __declspec(naked) _catchException0()
	{
		SAVE_REGISTERS1();
		SAVE_REGISTERS2();
		__asm push 0
		_remcomHandler();
	}
	/* Same thing for exception 4.  */
	void __declspec(naked) _catchException4()
	{
		SAVE_REGISTERS1();
		SAVE_REGISTERS2();
		__asm push 4
		_remcomHandler();
	}
	/* Same thing for exception 5.  */
	void __declspec(naked) _catchException5()
	{
		SAVE_REGISTERS1();
		SAVE_REGISTERS2();
		__asm push 5
		_remcomHandler();
	}
	/* Same thing for exception 6.  */
	void __declspec(naked) _catchException6()
	{
		SAVE_REGISTERS1();
		SAVE_REGISTERS2();
		__asm push 6
		_remcomHandler();
	}
	/* Same thing for exception 7.  */
	void __declspec(naked) _catchException7()
	{
		SAVE_REGISTERS1();
		SAVE_REGISTERS2();
		__asm push 7
		_remcomHandler();
	}
	/* Same thing for exception 8.  */
	void __declspec(naked) _catchException8()
	{
		SAVE_REGISTERS1();
		SAVE_ERRCODE();
		SAVE_REGISTERS2();
		__asm push 8
		_remcomHandler();
	}
	/* Same thing for exception 9.  */
	void __declspec(naked) _catchException9()
	{
		SAVE_REGISTERS1();
		SAVE_REGISTERS2();
		__asm push 9
		_remcomHandler();
	}

	/* Same thing for exception 10.  */
	void __declspec(naked) _catchException10()
	{
		SAVE_REGISTERS1();
		SAVE_ERRCODE();
		SAVE_REGISTERS2();
		__asm push 10
		_remcomHandler();
	}

	/* Same thing for exception 12.  */
	void __declspec(naked) _catchException12()
	{
		SAVE_REGISTERS1();
		SAVE_ERRCODE();
		SAVE_REGISTERS2();
		__asm push 12
		_remcomHandler();
	}

	/* Same thing for exception 16.  */
	void __declspec(naked) _catchException16()
	{
		SAVE_REGISTERS1();
		SAVE_REGISTERS2();
		__asm push 16
		_remcomHandler();
	}

	/* For 13, 11, and 14 we have to deal with the CHECK_FAULT stuff.  */

	/* Same thing for exception 13.  */
	void __declspec(naked) _catchException13 ()
	{
		if (!mem_fault_routine)
		{
			mem_fault();
		}
		SAVE_REGISTERS1();
		SAVE_ERRCODE();
		SAVE_REGISTERS2();
		__asm push 13
		_remcomHandler();
	}

	/* Same thing for exception 11.  */
	void __declspec(naked) _catchException11 ()
	{
		if (!mem_fault_routine)
		{
			mem_fault();
		}
		SAVE_REGISTERS1();
		SAVE_ERRCODE();
		SAVE_REGISTERS2();
		__asm push 11
		_remcomHandler();
	}

	/* Same thing for exception 14.  */
	void __declspec(naked) _catchException14 ()
	{
		if (!mem_fault_routine)
		{
			mem_fault();
		}
		SAVE_REGISTERS1();
		SAVE_ERRCODE();
		SAVE_REGISTERS2();
		__asm push 14
		_remcomHandler();
	}

	/*
	 * This function does all command procesing for interfacing to gdb.
	 */
	void handle_exception (int exceptionVector);
	/*
	 * remcomHandler is a front end for handle_exception.  It moves the
	 * stack pointer into an area reserved for debugger use.
	 */
	void __declspec(naked) _remcomHandler()
	{
		__asm 
		{
			pop  eax				/* pop off return address     */
			pop  eax				/* get the exception number   */
			mov  esp, stackPtr		/* move to remcom stack area  */
			push eax				/* push exception onto stack  */
			call handle_exception	/* this never returns */
		}
	}

	int hex (char ch)
	{
		if ((ch >= 'a') && (ch <= 'f'))
		{
			return (ch - 'a' + 10);
		}

		if ((ch >= '0') && (ch <= '9'))
		{
			return (ch - '0');
		}

		if ((ch >= 'A') && (ch <= 'F'))
		{
			return (ch - 'A' + 10);
		}

		return (-1);
	}

	static char remcomInBuffer[BUFMAX];
	static char remcomOutBuffer[BUFMAX];

	/* scan for the sequence $<data>#<checksum>     */

	unsigned char *getpacket()
	{
		unsigned char *buffer =(LPBYTE) &remcomInBuffer[0];
		unsigned char checksum;
		unsigned char xmitcsum;
		int count;
		char ch;

		while (1)
		{
			/* wait around for the start character, ignore all other characters */
			while ((ch = getDebugChar ()) != '$');

		retry:
			checksum = 0;
			xmitcsum = -1;
			count = 0;

			/* now, read until a # or end of buffer is found */
			while (count < BUFMAX)
			{
				ch = getDebugChar ();
				if (ch == '$')
				{
					goto retry;
				}

				if (ch == '#')
				{
					break;
				}

				checksum = checksum + ch;
				buffer[count] = ch;
				count = count + 1;
			}

			buffer[count] = 0;

			if (ch == '#')
			{
				ch = getDebugChar ();
				xmitcsum = hex (ch) << 4;
				ch = getDebugChar ();
				xmitcsum += hex (ch);

				if (checksum != xmitcsum)
				{
					if (remote_debug)
					{
						xprintf("bad checksum.  My count = 0x%x, sent=0x%x. buf=%s\n",
						checksum, xmitcsum, buffer);
					}

					putDebugChar ('-');	/* failed checksum */
				}
				else
				{
					putDebugChar ('+');	/* successful transfer */

					/* if a sequence char is present, reply the sequence ID */
					if (buffer[2] == ':')
					{
						putDebugChar (buffer[0]);
						putDebugChar (buffer[1]);

						return &buffer[3];
					}

					return &buffer[0];
				}
			}
		}
	}

	/* send the packet in buffer.  */

	void putpacket (LPBYTE buffer)
	{
		unsigned char checksum;
		int count;
		char ch;

		/*  $<packet info>#<checksum>. */
		do
		{
			putDebugChar ('$');
			checksum = 0;
			count = 0;

			while (ch = buffer[count])
			{
				putDebugChar (ch);
				checksum += ch;
				count += 1;
			}

			putDebugChar ('#');
			putDebugChar (hexchars[checksum >> 4]);
			putDebugChar (hexchars[checksum % 16]);
		}
		while (getDebugChar() != '+');
	}

	void debug_error (char *format,char * parm = NULL)
	{
		if (remote_debug)
		{
			xprintf(format, parm);
		}
	}


	__inline void __declspec(naked) set_mem_err()
	{
		mem_err = 1;
	}

	/* These are separate functions so that they are so short and sweet
	   that the compiler won't save any registers (if there is a fault
	   to mem_fault, they won't get restored, so there better not be any
	   saved).  */
	#define get_char(addr)	*addr

	__inline void __declspec(naked) set_char (char *addr, int val)
	{
		*addr = val;
	}

	/* convert the memory pointed to by mem into hex, placing result in buf */
	/* return a pointer to the last char put in buf (null) */
	/* If MAY_FAULT is non-zero, then we should set mem_err in response to
	   a fault; if zero treat a fault like any other fault in the stub.  */
	char *mem2hex (char* mem, char* buf, int count, int may_fault)
	{
		int i;
		unsigned char ch;

		if (may_fault)
		{
			mem_fault_routine = set_mem_err;
		}
	
		for (i = 0; i < count; i++)
		{
			ch = get_char (mem++);
		
			if (may_fault && mem_err)
			{
				return (buf);
			}

			*buf++ = hexchars[ch >> 4];
			*buf++ = hexchars[ch % 16];
		}
	
		*buf = 0;
	
		if (may_fault)
		{
			mem_fault_routine = NULL;
		}
	
		return (buf);
	}

	/* convert the hex array pointed to by buf into binary to be placed in mem */
	/* return a pointer to the character AFTER the last byte written */
	char *hex2mem (char* buf, char* mem, int count, int may_fault)
	{
		int i;
		unsigned char ch;

		if (may_fault)
		{
			mem_fault_routine = set_mem_err;
		}

		for (i = 0; i < count; i++)
		{
			ch  = hex (*buf++) << 4;
			ch += hex (*buf++);

			set_char (mem++, ch);

			if (may_fault && mem_err)
			{
				return (mem);
			}
		}

		if (may_fault)
		{
			mem_fault_routine = NULL;
		}

		return (mem);
	}

	/* this function takes the 386 exception vector and attempts to
	   translate this number into a unix compatible signal value */
	int computeSignal (int exceptionVector)
	{
		int sigval;
		switch (exceptionVector)
		{
		case 0:
			sigval = 8;
			break;			/* divide by zero */
		case 1:
			sigval = 5;
			break;			/* debug exception */
		case 3:
			sigval = 5;
			break;			/* breakpoint */
		case 4:
			sigval = 16;
			break;			/* into instruction (overflow) */
		case 5:
			sigval = 16;
			break;			/* bound instruction */
		case 6:
			sigval = 4;
			break;			/* Invalid opcode */
		case 7:
			sigval = 8;
			break;			/* coprocessor not available */
		case 8:
			sigval = 7;
			break;			/* double fault */
		case 9:
			sigval = 11;
			break;			/* coprocessor segment overrun */
		case 10:
			sigval = 11;
			break;			/* Invalid TSS */
		case 11:
			sigval = 11;
			break;			/* Segment not present */
		case 12:
			sigval = 11;
			break;			/* stack exception */
		case 13:
			sigval = 11;
			break;			/* general protection */
		case 14:
			sigval = 11;
			break;			/* page fault */
		case 16:
			sigval = 7;
			break;			/* coprocessor error */
		default:
			sigval = 7;		/* "software generated" */
		}

		return (sigval);
	}

	/**********************************************/
	/* WHILE WE FIND NICE HEX CHARS, BUILD AN INT */
	/* RETURN NUMBER OF CHARS PROCESSED           */
	/**********************************************/
	int hexToInt (char **ptr, int *intValue)
	{
		int numChars = 0;
		int hexValue;

		*intValue = 0;

		while (**ptr)
		{
			hexValue = hex (**ptr);
			if (hexValue >= 0)
			{
				*intValue = (*intValue << 4) | hexValue;
				numChars++;
			}
			else
			{
				break;
			}

			(*ptr)++;
		}

		return (numChars);
	}

	
	/* this function is used to set up exception handlers for tracing and
	   breakpoints */
	void set_debug_traps()
	{
		stackPtr = &remcomStack[STACKSIZE / sizeof (int) - 1];

		exceptionHandler (0, _catchException0);
		exceptionHandler (1, _catchException1);
		exceptionHandler (3, _catchException3);
		exceptionHandler (4, _catchException4);
		exceptionHandler (5, _catchException5);
		exceptionHandler (6, _catchException6);
		exceptionHandler (7, _catchException7);
		exceptionHandler (8, _catchException8);
		exceptionHandler (9, _catchException9);
		exceptionHandler (10, _catchException10);
		exceptionHandler (11, _catchException11);
		exceptionHandler (12, _catchException12);
		exceptionHandler (13, _catchException13);
		exceptionHandler (14, _catchException14);
		exceptionHandler (16, _catchException16);

		initialized = 1;
	}

	/* This function will generate a breakpoint exception.  It is used at the
	   beginning of a program to sync up with a debugger and can be used
	   otherwise as a quick means to stop program execution and "break" into
	   the debugger. */

	 void __declspec(naked) breakpoint ()
	{
		if (initialized)
		{
			BREAKPOINT();
		}
	}

	/*
	 * This function does all command procesing for interfacing to gdb.
	 */
	void handle_exception (int exceptionVector)
	{
		int sigval, stepping;
		int addr, length;
		char *ptr;
		int newPC;

		gdb_i386vector = exceptionVector;

		if (remote_debug)
		{
			xprintf ("vector=%d, sr=0x%x, pc=0x%x\n", exceptionVector, registers[PS], registers[PC]);
		}

		/* reply to host that an exception has occurred */
		sigval = computeSignal (exceptionVector);

		ptr = remcomOutBuffer;

		*ptr++ = 'T';			/* notify gdb with signo, PC, FP and SP */
		*ptr++ = hexchars[sigval >> 4];
		*ptr++ = hexchars[sigval & 0xf];

		*ptr++ = hexchars[ESP]; 
		*ptr++ = ':';
		ptr = mem2hex((char *)&registers[ESP], ptr, 4, 0);	/* SP */
		*ptr++ = ';';

		*ptr++ = hexchars[EBP]; 
		*ptr++ = ':';
		ptr = mem2hex((char *)&registers[EBP], ptr, 4, 0); 	/* FP */
		*ptr++ = ';';

		*ptr++ = hexchars[PC]; 
		*ptr++ = ':';
		ptr = mem2hex((char *)&registers[PC], ptr, 4, 0); 	/* PC */
		*ptr++ = ';';

		*ptr = '\0';

		putpacket ((LPBYTE)remcomOutBuffer);

		stepping = 0;

		while (true)
		{
			remcomOutBuffer[0] = 0;
			ptr = (char*)getpacket ();

			switch (*ptr++)
			{
			case '?':
				remcomOutBuffer[0] = 'S';
				remcomOutBuffer[1] = hexchars[sigval >> 4];
				remcomOutBuffer[2] = hexchars[sigval % 16];
				remcomOutBuffer[3] = 0;
				break;
			case 'd':
				remote_debug = !(remote_debug);	/* toggle debug flag */
				break;
			case 'g':		/* return the value of the CPU registers */
				mem2hex ((char *) registers, remcomOutBuffer, NUMREGBYTES, 0);
				break;
			case 'G':		/* set the value of the CPU registers - return OK */
				hex2mem (ptr, (char *) registers, NUMREGBYTES, 0);
				xstrcpy (remcomOutBuffer, "OK");
				break;
			case 'P':		/* set the value of a single CPU register - return OK */
			{
				int regno;

				if (hexToInt (&ptr, &regno) && *ptr++ == '=')
				{
					if (regno >= 0 && regno < NUMREGS)
					{
						hex2mem (ptr, (char *) &registers[regno], 4, 0);
						xstrcpy (remcomOutBuffer, "OK");
						break;
					}
				}

				xstrcpy (remcomOutBuffer, "E01");
				break;
			}

			/* mAA..AA,LLLL  Read LLLL bytes at address AA..AA */
			case 'm':
				/* TRY TO READ %x,%x.  IF SUCCEED, SET PTR = 0 */
				if (hexToInt (&ptr, &addr))
				{
					if (*(ptr++) == ',')
					{
						if (hexToInt (&ptr, &length))
						{
							ptr = 0;
							mem_err = 0;
							mem2hex ((char *) addr, remcomOutBuffer, length, 1);
							if (mem_err)
							{
								xstrcpy (remcomOutBuffer, "E03");
								debug_error ("memory fault");
							}
						}
					}
				}

				if (ptr)
				{
					xstrcpy (remcomOutBuffer, "E01");
				}

				break;

			/* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
			case 'M':
				/* TRY TO READ '%x,%x:'.  IF SUCCEED, SET PTR = 0 */
				if (hexToInt (&ptr, &addr))
				{
					if (*(ptr++) == ',')
					{
						if (hexToInt (&ptr, &length))
						{
							if (*(ptr++) == ':')
							{
								mem_err = 0;
								hex2mem (ptr, (char *) addr, length, 1);

								if (mem_err)
								{
									xstrcpy (remcomOutBuffer, "E03");
									debug_error ("memory fault");
								}
								else
								{
									xstrcpy (remcomOutBuffer, "OK");
								}

								ptr = 0;
							}
						}
					}
				}

				if (ptr)
				{
					xstrcpy (remcomOutBuffer, "E02");
				}

				break;

			/* cAA..AA    Continue at address AA..AA(optional) */
			/* sAA..AA   Step one instruction from AA..AA(optional) */
			case 's':
				stepping = 1;

			case 'c':
				/* try to read optional parameter, pc unchanged if no parm */
				if (hexToInt (&ptr, &addr))
				{
					registers[PC] = addr;
				}

				newPC = registers[PC];

				/* clear the trace bit */
				registers[PS] &= 0xfffffeff;

				/* set the trace bit if we're stepping */
				if (stepping)
				{
					registers[PS] |= 0x100;
				}

				return_to_prog ();	/* this is a jump */

				break;

			/* kill the program */
			case 'k':		/* do nothing */
				break;
			}			/* switch */

			/* reply to the request */
			putpacket ((LPBYTE)remcomOutBuffer);
		}
	}

}